package sample.util;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.servlet.ServletContext;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.support.XmlWebApplicationContext;


public class BeanHelper {
    /* Instance of bean factory created once */
    private static DefaultListableBeanFactory factory;

    /* Private constructor */
    private BeanHelper() {
        // Do nothing.
    }

    /**
     * Return the Spring BeanFactory instance.
     *
     * @return The BeanFactory instance.
     */
    public static DefaultListableBeanFactory getBeanFactory() {
        return factory;
    }

    /**
     * Initializes the bean factory.
     *
     * @param context The servlet context. This will pick the Spring bean
     *  configuration files
     */
    public static void init(ServletContext context) {
        BeanHelper.factory = (DefaultListableBeanFactory) ((XmlWebApplicationContext) WebApplicationContextUtils.getRequiredWebApplicationContext(context)).getBeanFactory();

        Properties prop = ConventionUtils.getConfigProperties();

        List beanMatches = ConventionUtils.getBeanMatches(prop);
        parseBeanMatches(beanMatches, prop);
    }

    private static void parseBeanMatches(List beanMatches, Properties prop) {
        for (Iterator iter = beanMatches.iterator(); iter.hasNext();) {
            BeanMatch match = (BeanMatch) iter.next();

            Iterator it = match.getMatchingClassMethMap().entrySet().iterator();

            while (it.hasNext()) {
                Map.Entry pairs = (Map.Entry) it.next();

                String className = (String) pairs.getKey();

                String classNameSuffix = prop.getProperty(Constants.CONVENTION_BEAN_CLASS_NAME_ENDS_WITH);

                if (className.endsWith("Action")) {
                    //ignore
                } else if (className.endsWith(classNameSuffix)) {
                    int lastDotLen = className.lastIndexOf(".");
                    int suffixLen = className.lastIndexOf(classNameSuffix);

                    String classNameStripped = className.substring(lastDotLen +
                            1, suffixLen);

                    String beanId = camel(classNameStripped);
                    System.out.println("Creating bean with ...... ");
                    System.out.println(" ------ Bean Id  " + beanId);

                    System.out.println(" ------ Bean class name " + className);

                    //Create Bean
                    BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(className);

                    //Set dependent beans
                    List injectMethods = (List) pairs.getValue();

                    for (Iterator methIter = injectMethods.iterator();
                            methIter.hasNext();) {
                        String methodName = (String) methIter.next();
                        String beanName = getBeanNameFromMethodName(methodName,
                                prop);
                        System.out.println(" ------------- Dependency is " +
                            beanName);
                        builder.addPropertyReference(beanName, beanName);
                    }

                    factory.registerBeanDefinition(beanId,
                        builder.getBeanDefinition());
                }
            }
        }
    }

    private static String getBeanNameFromMethodName(String methodName,
        Properties prop) {
        String injectPrefixStripped = methodName.replaceFirst(prop.getProperty(
                    Constants.CONVENTION_INJECT_METHOD_NAME_BEGINS_WITH), "");

        return camel(injectPrefixStripped);
    }

    private static String camel(String input) {
        return input.substring(0, 1).toLowerCase()
                    .concat(input.substring(1, input.length()));
    }
}
